#include "EqnParser.h"

EqnParser::EqnParser(const char *eqn)
{
    strncpy(eqnStr,eqn,MAX_LEN);
    registers.reset();
    RPN.reset();
    processEqn(eqnStr);
    recDescent(eqnStr);
    RPN.push('\0');
}

void EqnParser::processEqn(char *str)
{
    char* p=str;
    float temp;
    int n;
    do{
        if(isdigit(*p))
        {
            sscanf(p,"%f",&temp);
            *p='A'+registers.size();

            //check for int or float/////
            while(isdigit( *(++p) )) *p=' ';
            if(*p=='.')
            {
                *p=' ';
                while(isdigit( *(++p) )) *p=' ';
            }
            ///////////////////////////////

            registers.push(temp);
        }
        else if(n=checkAhead(p,"sin")){clearAhead(p,n);*p=SIN;p+=n;}
        else if(n=checkAhead(p,"cos")){clearAhead(p,n);*p=COS;p+=n;}
        else if(n=checkAhead(p,"tan")){clearAhead(p,n);*p=TAN;p+=n;}
        else if(n=checkAhead(p,"sqrt")){clearAhead(p,n);*p=SQRT;p+=n;}
        else if(n=checkAhead(p,"fabs")){clearAhead(p,n);*p=FABS;p+=n;}
        else if(*p=='x') *p='[';
        else if(*p=='y') *p='\\';
        else if(*p=='z') *p=']';

    }while(*p++);
}

int EqnParser::recDescent(char *eqn)
{
    OPSTACK opStack;
    char* p = eqn;
    int n=0,offset;
    do{
        if(*p==' ')continue;
        else if(*p=='(')
        {
            offset=recDescent(++p);
            p+=offset;
            n+=offset+1;
        }
        else if(isFn(p))
        {
            opStack.push(*p++);
            offset=recDescent(++p);
            p+=offset;
            n+=offset+2;
        }
        else if(*p==')'){
            while(!opStack.empty())RPN.push(opStack.pop());
            return n;
        }
        else if(isOperation(*p))
        {
            if(!opStack.empty() && prec(*p) < prec(opStack.peek()))
               while(!opStack.empty())RPN.push(opStack.pop());
            opStack.push(*p);
        }
        else if(isSymbol(*p))RPN.push(*p);

    }while(n++,*p++);

    while(!opStack.empty())RPN.push(opStack.pop());

    return n;
}

float EqnParser::evalRPN(char *rpn){
    VARSTACK varStack;
    REGSET regs = registers;
    char* p =rpn;
    varStack.reset();
    while(*p)
    {

        while(*p && !isOperation(*p))
            varStack.push(*p++);

        char var1 = varStack.pop();
        float temp = regs[toLoc(var1)];


        if(isFunc(*p)){
            switch(*p)
            {
            case SIN:
                temp=sin(temp);break;
            case COS:
                temp=cos(temp);break;
            case TAN:
                temp=tan(temp);break;
            case SQRT:
                temp=sqrt(temp);break;
            case FABS:
                temp=fabs(temp);break;
            default:
            break;
            }
            regs[toLoc(var1)]=temp;
            varStack.push(var1);
            p++;
            continue;
        }

        if(varStack.empty())return temp;

        char var2=varStack.pop();
        float temp2=regs[toLoc(var2)];

        if(isOperator(*p)){
        switch(*p)
        {
        case PLUS:
            temp2+=temp;break;
        case MINUS:
            temp2-=temp;break;
        case MULTIPLY:
            temp2*=temp;break;
        case DIVIDE:
            temp2/=temp;break;
        case POW:
            temp2=pow(temp2,temp);break;
        case MOD:
            temp2=fmod(temp2,temp);break;
        default:
            break;
        }
        regs[toLoc(var2)]=temp2;
        varStack.push(var2);
        }
        p++;
    }

    return rpn==p?0:regs[toLoc(varStack.peek())];
}

float EqnParser::eval(int x, int y, int z){
    setVarX(x);
    setVarY(y);
    setVarZ(z);
    return evalRPN(RPN.ptr());
}
